#include "gb28181_register.h"
#include "../gb28181_service.h"
#include "../gb28181_utils.h"
#include "utils_log.h"
#include <sstream>
#include <strings.h>

namespace El {
namespace GB28181 {

void GB28181Register::SendRegister(ServerContext &server)
{
    if (!server.exosip_context)
        return;

    std::lock_guard<std::mutex> lock(server.mutex);
    EL_DEBUG("Sending register for device {} to server {}:{}", server.config.device_id, server.config.server_ip,
             server.config.server_port);

    std::string from = "sip:" + server.config.device_id + "@" + server.config.domain;
    std::string to = "sip:" + server.config.server_id + "@" + server.config.domain;
    std::string proxy = "sip:" + server.config.server_ip + ":" + std::to_string(server.config.server_port);

    osip_message_t *register_msg = nullptr;

    eXosip_lock(server.exosip_context);
    int rid = eXosip_register_build_initial_register(server.exosip_context, from.c_str(), proxy.c_str(), to.c_str(),
                                                     server.config.register_interval, &register_msg);

    if (rid > 0 && register_msg) {
        eXosip_register_send_register(server.exosip_context, rid, register_msg);
        server.registration_id = rid;
        server.last_register = std::chrono::steady_clock::now();
        EL_INFO("Sent initial register for device {} (rid={})", server.config.device_id, rid);
    } else {
        EL_ERROR("Failed to build initial register for device {}", server.config.device_id);
    }
    eXosip_unlock(server.exosip_context);
}

void GB28181Register::SendRegisterWithAuth(ServerContext &server)
{
    if (!server.exosip_context)
        return;

    EL_DEBUG("Sending register with authentication for device {}", server.config.device_id);
    std::string from = "sip:" + server.config.device_id + "@" + server.config.domain;
    std::string to = "sip:" + server.config.server_id + "@" + server.config.domain;
    std::string proxy = "sip:" + server.config.server_ip + ":" + std::to_string(server.config.server_port);

    osip_message_t *register_msg = nullptr;

    eXosip_lock(server.exosip_context);
    // Use the existing registration_id to build a new register message
    int rid = eXosip_register_build_register(server.exosip_context, server.registration_id,
                                             server.config.register_interval, &register_msg);

    if (rid > 0 && register_msg) {
        // Build and add Authorization header
        std::string auth_header = BuildAuthorizationHeader(server, "REGISTER", to);

        if (!auth_header.empty()) {
            osip_message_set_header(register_msg, "Authorization", auth_header.c_str());
            EL_DEBUG("Added Authorization header for device {}", server.config.device_id);
        }

        eXosip_register_send_register(server.exosip_context, rid, register_msg);
        server.registration_id = rid;
        server.last_register = std::chrono::steady_clock::now();
        EL_INFO("Sent register with authentication for device {} (rid={})", server.config.device_id, rid);
    } else {
        EL_ERROR("Failed to build register with auth for device {}", server.config.device_id);
    }
    eXosip_unlock(server.exosip_context);
}

void GB28181Register::HandleRegisterFailure(eXosip_event_t *event,
                                            ServerContext &server,
                                            std::function<void(const std::string &)> statusCallback)
{
    if (!event || !event->response) {
        EL_ERROR("Invalid event or response in HandleRegisterFailure");
        return;
    }

    std::lock_guard<std::mutex> lock(server.mutex);
    if (event->rid >= 0) {
        server.registration_id = event->rid;
    }
    EL_DEBUG("Handling register failure for device {}, status code: {}", server.config.device_id,
             event->response->status_code);

    // Check if it's a 401 Unauthorized response
    if (event->response->status_code == 401) {
        EL_DEBUG("Received 401 Unauthorized, processing authentication challenge");
        // Parse WWW-Authenticate header
        osip_www_authenticate_t *www_auth = nullptr;
        osip_message_get_www_authenticate(event->response, 0, &www_auth);

        if (www_auth && www_auth->auth_type && strcasecmp(www_auth->auth_type, "Digest") == 0) {
            EL_DEBUG("Processing Digest authentication for device {}", server.config.device_id);
            // Extract authentication parameters
            if (www_auth->realm) {
                server.realm = GB28181Utils::UnquoteString(www_auth->realm);
            }
            if (www_auth->nonce) {
                server.nonce = GB28181Utils::UnquoteString(www_auth->nonce);
            }
            if (www_auth->opaque) {
                server.opaque = GB28181Utils::UnquoteString(www_auth->opaque);
            }
            if (www_auth->algorithm) {
                server.algorithm = www_auth->algorithm;
            } else {
                server.algorithm = "MD5";
            }

            server.auth_required = true;
            EL_DEBUG("Authentication parameters extracted for device {}: realm={}, algorithm={}",
                     server.config.device_id, server.realm, server.algorithm);

            // Retry registration with authentication if we haven't exceeded retry count
            if (server.auth_retry_count < MAX_RETRY_COUNT) {
                server.auth_retry_count++;
                EL_INFO("Retrying authentication for device {} (attempt {}/{})", server.config.device_id,
                        server.auth_retry_count, MAX_RETRY_COUNT);
                if (statusCallback) {
                    statusCallback("Retrying with authentication");
                }

                // Send register with authentication
                SendRegisterWithAuth(server);
            } else {
                EL_ERROR("Authentication failed for device {} after {} retries", server.config.device_id,
                         MAX_RETRY_COUNT);
                if (statusCallback) {
                    statusCallback("Authentication failed after max retries");
                }
                server.auth_retry_count = 0;
            }
        } else {
            EL_ERROR("Invalid authentication challenge for device {}", server.config.device_id);
            if (statusCallback) {
                statusCallback("Invalid authentication challenge");
            }
        }
    } else {
        // Other failure reasons
        std::string error_msg = "Registration failed with status: " + std::to_string(event->response->status_code);
        EL_ERROR("Registration failed for device {}: {}", server.config.device_id, error_msg);
        if (statusCallback) {
            statusCallback(error_msg);
        }
        server.auth_retry_count = 0;
    }
}

bool GB28181Register::ParseAuthenticateHeader(const std::string &authenticate, ServerContext &server)
{
    // Parse Digest authentication header
    size_t pos = authenticate.find("Digest ");
    if (pos == std::string::npos) {
        return false;
    }

    std::string params = authenticate.substr(pos + 7);

    // Parse realm
    pos = params.find("realm=\"");
    if (pos != std::string::npos) {
        size_t end = params.find("\"", pos + 7);
        if (end != std::string::npos) {
            server.realm = params.substr(pos + 7, end - pos - 7);
        }
    }

    // Parse nonce
    pos = params.find("nonce=\"");
    if (pos != std::string::npos) {
        size_t end = params.find("\"", pos + 7);
        if (end != std::string::npos) {
            server.nonce = params.substr(pos + 7, end - pos - 7);
        }
    }

    // Parse opaque (optional)
    pos = params.find("opaque=\"");
    if (pos != std::string::npos) {
        size_t end = params.find("\"", pos + 8);
        if (end != std::string::npos) {
            server.opaque = params.substr(pos + 8, end - pos - 8);
        }
    }

    // Parse algorithm (optional, default MD5)
    pos = params.find("algorithm=");
    if (pos != std::string::npos) {
        size_t end = params.find_first_of(", ", pos + 10);
        if (end != std::string::npos) {
            server.algorithm = params.substr(pos + 10, end - pos - 10);
        } else {
            server.algorithm = params.substr(pos + 10);
        }
    } else {
        server.algorithm = "MD5";
    }

    return !server.realm.empty() && !server.nonce.empty();
}

std::string GB28181Register::BuildAuthorizationHeader(const ServerContext &server,
                                                      const std::string &method,
                                                      const std::string &uri)
{
    if (!server.auth_required || server.realm.empty() || server.nonce.empty()) {
        return "";
    }

    // Calculate response using the GB28181Utils function
    std::string response = GB28181Utils::CalculateResponse(server.config.device_id, server.realm,
                                                           server.config.password, server.nonce, method, uri);

    // Build Authorization header
    std::stringstream auth;
    auth << "Digest username=\"" << server.config.device_id << "\", " << "realm=\"" << server.realm << "\", "
         << "nonce=\"" << server.nonce << "\", " << "uri=\"" << uri << "\", " << "response=\"" << response << "\", "
         << "algorithm=" << server.algorithm;

    if (!server.opaque.empty()) {
        auth << ", opaque=\"" << server.opaque << "\"";
    }

    return auth.str();
}

} // namespace GB28181
} // namespace El
